<!--
 * @description 复杂表格组件，支持多级表头、合并单元格、自定义样式和PPT导出
 * @author [作者名]
 * @date 2024-03-xx
 * @features
 * 1. 支持多年份数据对比
 * 2. 支持同比数据展示和颜色标记
 * 3. 支持自定义样式配置
 * 4. 支持PPT导出功能
 * 5. 支持新旧数据格式兼容
 -->
<template>
  <!-- 表格容器 -->
  <div class="complex-table">
    <div class="normal-complex-table" :style="{ width: config.tableWidth, height: config.tableHeight }">
      <!-- 主表格，使用配置的宽度 -->
      <table class="custom-table">
        <!-- 表头部分 -->
        <thead>
          <tr :style="{ height: config.headerHeight }">
            <!-- 遍历渲染表头列 -->
            <th v-for="header in tableConfig.headers" :key="header.key" :style="getHeaderStyle(header)">
              {{ header.title }}
            </th>
          </tr>
        </thead>
        <!-- 表格主体部分 -->
        <tbody>
          <!-- 遍历渲染数据行，按分类分组 -->
          <template v-for="category in tableConfig.data" :key="category.category.name">
            <tr v-for="(row, rowIndex) in category.rows" :key="row.indicator">
              <!-- 分类列，只在每个分类的第一行显示，使用rowspan合并单元格 -->
              <td v-if="rowIndex === 0" :rowspan="category.category.rowspan" :style="categoryStyle" class="category-cell">
                {{ category.category.name }}
              </td>
              <!-- 指标列 -->
              <td :style="contentStyle">
                {{ row.indicator }}
              </td>
              <!-- 数据列和同比列 -->
              <td v-for="header in tableConfig.headers.slice(2)" :key="header.key" :style="getCellStyle(header, row)">
                {{ row[header.key] }}
              </td>
            </tr>
          </template>
        </tbody>
      </table>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ComplexTable',
  props: {
    /**
     * API数据源
     */
    apiData: {
      type: Array,
      default: function () {
        return [];
      },
    },
    /**
     * 表格样式配置
     */
    config: {
      type: Object,
      default: function () {
        return {
          /** 表格整体宽度，支持百分比或具体像素值 */
          tableWidth: '100%',
          /** 表格整体高度，支持百分比或具体像素值 */
          tableHeight: '100%',
          /** 表格边框宽度 */
          borderWidth: '1px',
          /** 表格边框样式：solid实线、dashed虚线、dotted点线等 */
          borderStyle: 'solid',
          /** 表格边框颜色 */
          borderColor: '#000',
          /** 单元格内边距 */
          cellPadding: '0',
          /** 表头行高 */
          headerHeight: '60px',
          /** 表头背景色 */
          headerBgColor: '#4874cb',
          /** 表头文字颜色 */
          headerTextColor: '#ffffff',
          /** 表头文字大小 */
          headerFontSize: '18px',
          /** 表头文字粗细：400正常、500中等、600半粗、700粗体 */
          headerFontWeight: '500',
          /** 表头文字水平对齐：left左对齐、center居中、right右对齐 */
          headerAlign: 'center',
          /** 表头文字垂直对齐：top顶部、middle居中、bottom底部 */
          headerVerticalAlign: 'middle',
          /** 分类列宽度 */
          categoryWidth: '80px',
          /** 分类列背景色 */
          categoryBgColor: '#4874cb',
          /** 分类列文字颜色 */
          categoryTextColor: '#ffffff',
          /** 分类列文字大小 */
          categoryFontSize: '18px',
          /** 分类列文字粗细 */
          categoryFontWeight: '500',
          /** 分类列文字水平对齐 */
          categoryAlign: 'center',
          /** 分类列文字垂直对齐 */
          categoryVerticalAlign: 'middle',
          /** 内容区域背景色 */
          contentBgColor: '#ffffff',
          /** 内容区域文字颜色 */
          contentTextColor: '#333333',
          /** 内容区域文字大小 */
          contentFontSize: '14px',
          /** 内容区域文字粗细 */
          contentFontWeight: '600',
          /** 内容区域文字水平对齐 */
          contentAlign: 'center',
          /** 数值列文字水平对齐 */
          valueAlign: 'center',
          /** 内容区域文字垂直对齐 */
          contentVerticalAlign: 'middle',
          /** 鼠标悬停时的背景色 */
          hoverBgColor: '#f5f7fa',
        };
      },
    },
    /**
     * PPT导出配置
     */
    pptConfig: {
      type: Object,
      default: function () {
        return {
          /** 页面尺寸(英寸) */
          pageSize: { width: 10, height: 5.62 },
          /** 表格配置 */
          table: {
            x: 0,
            y: 0,
            colW: [0.5, 1.6, 1, 1, 1],
            fill: { color: 'F9F9F9' }, // 填充颜色
            color: '000000', // 文字颜色
            fontSize: 9, // 字体大小
            border: { pt: 0.5, color: '000000' }, // 边框样式
            align: 'center', // 水平对齐
            valign: 'middle', // 垂直对齐
            margin: 2, // 边距
            autoPage: false, // 自动分页
          },
          /** 标题配置 */
          title: {
            x: 0.5, // 标题X坐标
            y: 0, // 标题Y坐标
            w: 9.0, // 标题宽度
            h: 0.4, // 标题高度
            fontSize: 14, // 字体大小
            bold: true, // 是否加粗
            color: '363636', // 文字颜色
            align: 'center', // 对齐方式
          },
          /** 表头样式 */
          header: {
            fill: { color: '#4874cb' }, // 填充颜色
            color: 'FFFFFF', // 文字颜色
            colspan: 1, // 跨列数
          },
          /** 分类列样式 */
          category: {
            fill: { color: '#4874cb' }, // 填充颜色
            color: 'FFFFFF', // 文字颜色
          },
          /** 错误提示样式 */
          error: {
            x: 0.5, // X坐标
            y: 2, // Y坐标
            w: 9, // 宽度
            h: 0.5, // 高度
            fontSize: 12, // 字体大小
            color: 'FF0000', // 文字颜色
            align: 'center', // 对齐方式
          },
        };
      },
    },
    /**
     * 数据字段映射配置
     */
    fieldMapping: {
      type: Object,
      default: function () {
        return {
          /** 大类字段名 */
          category: 'category',
          /** 指标字段名 */
          indicator: 'indicator',
          /** 年份字段前缀 */
          yearPrefix: 'year',
          /** 同比值字段名 */
          ratioValue: 'ratioValue',
          /** 同比颜色字段名 */
          ratioColor: 'ratioColor',
        };
      },
    },
  },
  data() {
    return {
      /**
       * 表格配置对象，包含表头和数据
       */
      tableConfig: this.convertApiDataToTableConfig(this.apiData),
    };
  },
  watch: {
    /**
     * 监听apiData变化，自动更新表格配置
     */
    apiData: {
      handler(newVal) {
        this.tableConfig = this.convertApiDataToTableConfig(newVal);
      },
      deep: true,
      immediate: true,
    },
  },
  computed: {
    /**
     * 分类列样式计算属性
     * @returns {Object} 样式对象
     */
    categoryStyle() {
      return {
        backgroundColor: this.config.categoryBgColor,
        color: this.config.categoryTextColor,
        fontSize: this.config.categoryFontSize,
        fontWeight: this.config.categoryFontWeight,
        padding: this.config.cellPadding,
        width: this.config.categoryWidth,
        borderWidth: this.config.borderWidth,
        borderStyle: this.config.borderStyle,
        borderColor: this.config.borderColor,
        textAlign: this.config.categoryAlign,
        verticalAlign: this.config.categoryVerticalAlign,
      };
    },
    /**
     * 内容单元格样式计算属性
     * @returns {Object} 样式对象
     */
    contentStyle() {
      return {
        backgroundColor: this.config.contentBgColor,
        color: this.config.contentTextColor,
        fontSize: this.config.contentFontSize,
        fontWeight: this.config.contentFontWeight,
        padding: this.config.cellPadding,
        borderWidth: this.config.borderWidth,
        borderStyle: this.config.borderStyle,
        borderColor: this.config.borderColor,
        textAlign: this.config.contentAlign,
        verticalAlign: this.config.contentVerticalAlign,
        height: this.contentRowHeight,
      };
    },
    /**
     * 计算内容区域每行的高度(预览表格)
     * @returns {number|string}
     */
    contentRowHeight() {
      if (!this.apiData || !this.apiData.length) return 'auto';
      return ((1080 * parseFloat(this.config.tableHeight)) / 100 - parseFloat(this.config.headerHeight)) / this.apiData.length;
    },
    /**
     * 计算内容区域每行的高度(PPT表格)
     * @returns {number}
     */
    pptContentRowHeight() {
      if (!this.apiData || !this.apiData.length) return 0;
      return ((this.contentRowHeight / 1080) * this.pptConfig.pageSize.height).toFixed(2) * 1;
    },
  },
  methods: {
    /**
     * 将旧格式数据转换为新格式
     * @description 处理旧版本的数据格式，转换为标准化的新格式数据结构
     * @param {Array} apiData - 原始API数据
     * @returns {Array} - 标准化后的数据数组
     */
    normalizeApiData(apiData) {
      if (!apiData || !apiData.length) return [];
      var firstItem = apiData[0];
      var isOldFormat = '大类' in firstItem || '指标' in firstItem;
      if (!isOldFormat) return apiData;
      return apiData.map(function (item) {
        var newItem = {
          category: item['大类'],
          indicator: item['指标'],
        };
        Object.keys(item).forEach(function (key) {
          if (!isNaN(Number(key))) {
            newItem['year' + key] = item[key];
          }
          if (key.indexOf('同比') !== -1 && key.indexOf('%') !== -1) {
            newItem.ratioValue = item[key];
          }
          if (key.indexOf('颜色') !== -1) {
            newItem.ratioColor = item[key];
          }
        });
        return newItem;
      });
    },
    /**
     * 从API数据中提取年份信息
     * @description 从数据中提取所有的年份字段，并按照年份排序
     * @param {Array} apiData - API返回的原始数据
     * @returns {Array<string>} - 提取的年份列表
     */
    extractYearsFromData(apiData) {
      if (!apiData || !apiData.length) return [];
      var normalizedData = this.normalizeApiData(apiData);
      var firstItem = normalizedData[0];
      var yearPrefix = this.fieldMapping.yearPrefix;
      return Object.keys(firstItem)
        .filter(function (key) {
          return key.indexOf(yearPrefix) === 0;
        })
        .map(function (key) {
          return key.replace(yearPrefix, '');
        })
        .sort();
    },
    /**
     * 查找对应的颜色键
     * @description 根据不同策略查找数据项中的颜色字段
     * @param {Object} item - 数据项
     * @param {string} year - 年份
     * @param {string} nextYear - 下一年份
     * @param {number} index - 当前索引
     * @returns {string|null} - 颜色键名或null
     */
    findColorKey(item, year, nextYear, index) {
      if (this.fieldMapping.ratioColor in item) {
        return this.fieldMapping.ratioColor;
      }
      var yearSpecificKey = Object.keys(item).find(function (key) {
        return key.indexOf('颜色') !== -1 && key.indexOf(year) !== -1 && key.indexOf(nextYear) !== -1;
      });
      if (yearSpecificKey) return yearSpecificKey;
      var ratioIndexKey = Object.keys(item).find(function (key) {
        return key.indexOf('颜色') !== -1 && key.indexOf('同比' + (index + 1)) !== -1;
      });
      if (ratioIndexKey) return ratioIndexKey;
      var ratioKey = Object.keys(item).find(function (key) {
        return key.indexOf('颜色') !== -1 && key.indexOf('同比') !== -1;
      });
      if (ratioKey) return ratioKey;
      var colorKeys = Object.keys(item).filter(function (key) {
        return key.indexOf('颜色') !== -1;
      });
      return colorKeys.length > index ? colorKeys[index] : null;
    },
    /**
     * 将API数据转换为表格配置格式
     * @description 处理原始数据，生成表格所需的表头和数据结构
     * @param {Array} apiData - API返回的原始数据
     * @returns {Object} - 包含headers和data的表格配置对象
     */
    convertApiDataToTableConfig(apiData) {
      if (!apiData || !apiData.length) {
        return { headers: [], data: [] };
      }
      var normalizedData = this.normalizeApiData(apiData);
      var categoryField = this.fieldMapping.category;
      var indicatorField = this.fieldMapping.indicator;
      var yearPrefix = this.fieldMapping.yearPrefix;
      var years = this.extractYearsFromData(apiData);
      var headers = [
        { key: 'category', title: '大类' },
        { key: 'indicator', title: '指标' },
      ];
      years.forEach((year, index) => {
        headers.push({ key: year, title: year, bisect: true });
        if (index < years.length - 1) {
          var nextYear = years[index + 1];
          headers.push({ key: 'ratio_' + year + '_' + nextYear, title: '同比' });
        }
      });
      var totalWidth = 10 - this.pptConfig.table.x * 2;
      var fixedWidthSum = headers.reduce(function (sum, header) {
        return sum + (header.width || 0);
      }, 0);
      var bisectColumns = headers.filter(function (header) {
        return header.bisect;
      });
      if (bisectColumns.length > 0) {
        var remainingWidth = totalWidth - fixedWidthSum;
        var bisectWidth = remainingWidth / bisectColumns.length;
        headers.forEach(function (header) {
          if (header.bisect) header.width = bisectWidth;
        });
      }
      var categoryGroups = normalizedData.reduce(function (groups, item) {
        var category = item[categoryField];
        if (!groups[category]) groups[category] = [];
        groups[category].push(item);
        return groups;
      }, {});
      var data = Object.entries(categoryGroups).map(([categoryName, categoryItems]) => {
        var categoryData = {
          category: {
            name: categoryName,
            rowspan: categoryItems.length,
          },
          rows: categoryItems.map(item => {
            var row = { indicator: item[indicatorField] };
            years.forEach((year, index) => {
              var yearKey = yearPrefix + year;
              row[year] = item[yearKey] ? item[yearKey].toString() : '';
              if (index < years.length - 1) {
                var nextYear = years[index + 1];
                var ratioColumnKey = 'ratio_' + year + '_' + nextYear;
                var colorKey = this.findColorKey(item, year, nextYear, index);
                var ratioValue = item[this.fieldMapping.ratioValue];
                var formattedRatio = ratioValue !== undefined ? (ratioValue > 0 ? '+' + ratioValue + '%' : ratioValue + '%') : '';
                var ratioColor = colorKey ? item[colorKey] || '' : '';
                row[ratioColumnKey] = formattedRatio;
                row[ratioColumnKey + '_color'] = ratioColor;
              }
            });
            return row;
          }),
        };
        return categoryData;
      });
      return { headers: headers, data: data };
    },
    /**
     * 获取表头样式
     * @description 根据配置和表头信息生成表头单元格的样式
     * @param {Object} header - 表头配置对象
     * @returns {Object} 样式对象
     */
    getHeaderStyle(header) {
      return {
        backgroundColor: this.config.headerBgColor,
        color: this.config.headerTextColor,
        fontSize: this.config.headerFontSize,
        fontWeight: this.config.headerFontWeight,
        padding: this.config.cellPadding,
        width: header.key === 'category' ? this.config.categoryWidth : 'auto',
        borderWidth: this.config.borderWidth,
        borderStyle: this.config.borderStyle,
        borderColor: this.config.borderColor,
        textAlign: this.config.headerAlign,
        verticalAlign: this.config.headerVerticalAlign,
      };
    },
    /**
     * 获取单元格样式
     * @description 根据配置、表头信息和行数据生成单元格样式
     * @param {Object} header - 表头配置对象
     * @param {Object} row - 行数据对象
     * @returns {Object} 样式对象
     */
    getCellStyle(header, row) {
      return {
        backgroundColor: this.config.contentBgColor,
        color: header.key.indexOf('ratio_') === 0 && row[header.key + '_color'] ? row[header.key + '_color'] : this.config.contentTextColor,
        fontSize: this.config.contentFontSize,
        fontWeight: this.config.contentFontWeight,
        padding: this.config.cellPadding,
        borderWidth: this.config.borderWidth,
        borderStyle: this.config.borderStyle,
        borderColor: this.config.borderColor,
        textAlign: this.config.valueAlign || this.config.contentAlign,
        verticalAlign: this.config.contentVerticalAlign,
        height: this.contentRowHeight,
      };
    },
    /**
     * 导出为PPT
     * @description 将表格数据导出为PPT格式
     * @param {Object} pptx - pptxgen实例
     * @param {Object} slide - 幻灯片实例
     * @throws {Error} 导出过程中的错误
     */
    exportToPPT(pptx, slide) {
      try {
        var pptTableData = [
          this.tableConfig.headers.map(header => ({
            text: header.title,
            options: Object.assign({}, this.pptConfig.header),
          })),
        ];
        this.tableConfig.data.forEach(category => {
          category.rows.forEach((row, index) => {
            var rowData = [];
            if (index === 0) {
              rowData.push({
                text: category.category.name.replace(/(.)/g, '$1\n'),
                options: Object.assign({ rowspan: category.category.rowspan }, this.pptConfig.category),
              });
            }
            rowData.push({ text: row.indicator });
            this.tableConfig.headers.slice(2).forEach(header => {
              var text = row[header.key] || '';
              var options = {};
              if (header.key.indexOf('ratio_') === 0 && row[header.key + '_color']) {
                options.color = row[header.key + '_color'];
              }
              rowData.push({ text: text, options: options });
            });
            pptTableData.push(rowData);
          });
        });
        var pageSize = this.pptConfig.pageSize;
        var pptTableConfig = this.pptConfig.table;
        var titleConfig = this.pptConfig.title;
        var tableOptions = {
          x: pptTableConfig.x,
          y: pptTableConfig.y,
          colW: pptTableConfig.colW,
          // rowH: 1,
          fill: pptTableConfig.fill,
          color: pptTableConfig.color,
          fontSize: pptTableConfig.fontSize,
          border: pptTableConfig.border,
          align: pptTableConfig.align,
          valign: pptTableConfig.valign,
          margin: pptTableConfig.margin,
          autoPage: pptTableConfig.autoPage,
        };
        slide.addTable(pptTableData, tableOptions);
      } catch (error) {
        console.error('导出表格错误:', error);
        slide.addText('导出表格错误: ' + error.message, this.pptConfig.error);
      }
    },
  },
};
</script>

<style lang="less" scoped>
/* 复杂表格样式 */
.complex-table {
  width: 100%;
  height: 100%;
  /* 普通表格容器 */
  .normal-complex-table {
    position: relative;
    width: 100%;
    height: 100%;
    /* 自定义表格样式 */
    .custom-table {
      border-collapse: collapse; // 合并边框
      table-layout: fixed; // 固定表格布局
      width: 100%; // 宽度占满容器
      height: 100%;
      /* 分类单元格样式 */
      .category-cell {
        white-space: pre-line; // 保留换行符
        //文字竖向
        writing-mode: vertical-rl;
      }

      /* 表头和单元格过渡效果 */
      th,
      td {
        transition: background-color 0.2s ease; // 背景色过渡动画
      }
      /* 表格主体行样式 */
      tbody {
        tr {
          /* 鼠标悬停效果 */
          &:hover {
            td:not(.category-cell) {
              background-color: v-bind('config.hoverBgColor') !important; // 使用配置的悬停背景色
            }
          }
        }
      }
    }
  }
}
</style>
